package edu.biwu.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Proxy;

@Component
public class TransactionProxyFactory {

    @Autowired
    private TransactionManager transactionManager;

    @Autowired
    private ApplicationContext applicationContext; //注入spring容器

    /**
     *
     * @param cls 传递过来被代理类的字节码
     * @return
     * 例如:传递过来ProductServiceImpl.class,将来该方法就返回ProductServiceImpl代理类对象
     */
    @SuppressWarnings("unchecked") //不再检测方法中的警告
    public Object createProxyInstance(Class cls){
        if (cls.getInterfaces().length != 0) {//判断传递过来的被代理类是否实现接口,如果实现采用JDK动态代理方式
            return Proxy.newProxyInstance(
                    cls.getClassLoader(),//加载当前代理类
                    cls.getInterfaces(),//代理类和被代理类实现的接口

                    /**
                     * @param proxy  底层生成代理类对象
                     * @param method 通过代理类对象调用的方法封装成method对象
                     * @param args   通过代理类对象调用的方法传递的参数
                     * @return
                     * @throws Throwable
                     */
                    (proxy, method, args) -> {
                        Object reval = null;
                        //如果通过代理类对象调用的方法是find开头的方法(查询相关)
                        if (method.getName().startsWith("find")) {
                            //applicationContext.getBean(cls),applicationContext.getBean(cls根据Bean的类型去获取该Bean
                            reval = method.invoke(applicationContext.getBean(cls), args);
                        } else {
                            //如果通过代理类对象调用的方法不是find开头的方法(查询相关)
                            //我们才加入事务控制
                            try {
                                // productServiceImpl.增删改方法;
                                transactionManager.beginTransaction();
                                reval = method.invoke(applicationContext.getBean(cls), args);//假如cls=ProductServiceImpl.class
                                transactionManager.commitTansaction();
                            } catch (Exception e) {
                                transactionManager.rollbackTransaction();
                                e.printStackTrace();
                            } finally {
                                transactionManager.releaseConnection();
                            }
                        }
                        return reval;
                    }
            );
        }else {//该类没有实现任何接口,使用CGLIB动态代理 将被代理类作为父类
            //1.利用Enhancer增强被代理类,通过它生成代理类
            Enhancer enhancer = new Enhancer();

            //2.设置代理类的夫类
            enhancer.setSuperclass(cls);

            //3.设置回调函数,当通过代理类对象调用方法,转调回调函数
            enhancer.setCallback(
                    /**
                     *
                     * @param proxy 代理类对象
                     * @param method 通过代理类对象调用的方法封装成的Method对象
                     * @param args 通过代理类对象调用的方法传递的参数
                     * @param methodProxy 底层生成的代理类代理的方法
                     * @return
                     * @throws Throwable
                     */
                    (MethodInterceptor)(object, method, args, proxy) -> {
                        Object reval = null;
                        //如果通过代理类对象调用的方法是find开头的方法(查询相关)
                        if (method.getName().startsWith("find")) {
                            //applicationContext.getBean(cls),applicationContext.getBean(cls根据Bean的类型去获取该Bean
                            reval = method.invoke(applicationContext.getBean(cls), args);
                        } else {
                            //如果通过代理类对象调用的方法不是find开头的方法(查询相关)
                            //我们才加入事务控制
                            try {
                                // productServiceImpl.增删改方法;
                                transactionManager.beginTransaction();
                                reval = method.invoke(applicationContext.getBean(cls), args);//假如cls=ProductServiceImpl.class
                                transactionManager.commitTansaction();
                            } catch (Exception e) {
                                transactionManager.rollbackTransaction();
                                e.printStackTrace();
                            } finally {
                                transactionManager.releaseConnection();
                            }
                        }
                        return reval;
                    }
            );
            //4.返回当前代理类对象
            return enhancer.create();
        }
    }
}
